using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CodeIn.Diagnostics;
using AurelienRibon.Ui.Terminal;
using CodeIn.Ui;
using CodeIn.Core;
using AurelienRibon.Core.Options;
using System.Diagnostics;
using System.ComponentModel;

namespace CodeIn.Modules.Ui.Term {
	public partial class TermView : UserControl {
		public TermView() {
			InitializeComponent();

			Term.IsPromptInsertedAtLaunch = false;
			Term.IsSystemBeepEnabled = true;
			Term.Prompt = "\n> ";

			if (!DesignerProperties.GetIsInDesignMode(this)) {
				InitSomethingEntered();
				InitModuleRegistered();
				InitOptionRegistered();
				InitModuleExecutions();
				InitMessageHandlersCallbacks();
			}

			Term.InsertNewPrompt();
			Loaded += (s, e) => { Term.Focus(); };
		}

		// --------------------------------------------------------------------
		// Inits
		// --------------------------------------------------------------------

		private void InitSomethingEntered() {
			Term.CommandEntered += (s, e) => {
				if (!string.IsNullOrWhiteSpace(e.Command.Raw)) {
					// If $ is the first char, option mode must be activated, else it is
					// command mode.
					if (e.Command.Raw.StartsWith("$"))
						HandleOptionEntered(e.Command);
					else
						HandleCommandEntered(e.Command);
				} else {
					UiHelper.Synchronize(() => {
						Term.InsertNewPrompt();
					});
				}
			};
		}

		private void InitModuleRegistered() {
			// Registers all commands from modules already loaded
			foreach (var mod in ModuleManager.Instance.RegisteredModules) {
				string[] cmds = mod.Infos.Commands;
				foreach (string cmd in cmds)
					Term.RegisteredCommands.Add(cmd);
			}

			// Registers commands as they are dynamically added
			ModuleManager.Instance.ModuleRegistered += (s, e) => {
				string[] cmds = e.Module.Infos.Commands;
				foreach (string cmd in cmds)
					Term.RegisteredCommands.Add(cmd);
			};
		}

		private void InitOptionRegistered() {
			// Registers all options already registered
			foreach (var opt in OptionManager.Instance.RegisteredOptions.Values) {
				Term.RegisteredCommands.Add("$" + opt.Name);
			}

			// Registers options as they are dynamically added (should not happen !)
			OptionManager.Instance.OptionRegistered += (s, e) => {
				Debug.Assert(false, "All options should be registered in Initialize() calls, not dynamically.");
				Term.RegisteredCommands.Add("$" + e.Option.Name);
			};
		}

		private void InitModuleExecutions() {
			// Module execution test failed
			ModuleManager.Instance.ModuleExecutionTestEnded += (s, e) => {
				if (!Term.IsInputEnabled && !e.CanExecute) {
					if (ModuleManager.Instance.RunningModules.Count == 0) {
						UiHelper.Synchronize(() => {
							Term.InsertNewPrompt();
						});
					}
				}
			};

			// Module execution ended
			ModuleManager.Instance.ModuleExecutionEnded += (s, e) => {
				if (!Term.IsInputEnabled) {
					if (ModuleManager.Instance.RunningModules.Count == 0) {
						UiHelper.Synchronize(() => {
							Term.InsertNewPrompt();
						});
					}
				}
			};

			// Raw command triggered
			ModuleManager.Instance.RawCallTriggered += (s, e) => {
				// Only works if input is enabled
				if (Term.IsInputEnabled) {
					// Insert raw command after prompt
					if (Term.Text.Length > Term.LastPomptIndex)
						Term.Text = Term.Text.Remove(Term.LastPomptIndex);
					Term.Text += e.RawCommand;
					Term.CaretIndex = Term.Text.Length;

					// Simulate ENTER key press
					KeyEventArgs args = new KeyEventArgs(
						Keyboard.PrimaryDevice,
						Keyboard.PrimaryDevice.ActiveSource,
						DateTime.Now.TimeOfDay.Milliseconds,
						Key.Enter);
					args.RoutedEvent = Keyboard.PreviewKeyDownEvent;
					Term.RaiseEvent(args);
				}
			};
		}

		private void InitMessageHandlersCallbacks() {
			Term.Text += App.StartupLog;

			MessageHandlers.Trace.MessageWritten += (s, e) => {
				UiHelper.Synchronize(() => {
					if (!Term.IsInputEnabled)
						Term.Text += e.Message;
					else
						Term.InsertLineBeforePrompt(e.Message);
				});
			};

			MessageHandlers.Debug.MessageWritten += (s, e) => {
				UiHelper.Synchronize(() => {
					if (!Term.IsInputEnabled)
						Term.Text += e.Message;
					else
						Term.InsertLineBeforePrompt(e.Message);
				});
			};
		}

		// --------------------------------------------------------------------
		// Handles
		// --------------------------------------------------------------------

		private void HandleCommandEntered(Command command) {
			try {
				ModuleManager.Instance.CallAsync(command.Name, command.Args);
			} catch (ModuleManager.ModuleNotFoundException ex) {
				MessageHandlers.Trace.WriteErrorLine("'{0}' is not recognized as a valid command.", ex.CommandName);
				UiHelper.Synchronize(() => {
					Term.InsertNewPrompt();
				});
			}
		}

		private void HandleOptionEntered(Command command) {
			string line = command.Raw.Trim();

			if (line.Length == 1) {
				MessageHandlers.Trace.WriteErrorLine("You must provide an option name after '$'.");
				UiHelper.Synchronize(() => {
					Term.InsertNewPrompt();
				});
				return;
			}

			line = line.Substring(1).Trim();

			try {
				var parseResult = OptionManager.Instance.ParseAndSet(line);
				var opt = parseResult.ParsedOption;
				MessageHandlers.Trace.Write("{0} = {1}", opt.Name, opt.Value);
			} catch (OptionParseException ex) {
				MessageHandlers.Trace.WriteErrorLine("Invalid (and rather strange) option request.");
			} catch (OptionIsLockedException ex) {
				MessageHandlers.Trace.WriteErrorLine("Impossible to assign a new value, option is locked.");
			} catch (OptionNotRegisteredException ex) {
				MessageHandlers.Trace.WriteErrorLine("'{0}' is not a registered option.", ex.OptionName);
			} finally {
				UiHelper.Synchronize(() => {
					Term.InsertNewPrompt();
				});
			}
		}
	}
}
